package alp.starcode.nudtportal.user.service;

import alp.starcode.common.util.BeanUtils;
import alp.starcode.nudtportal.framework.base.BaseService;
import alp.starcode.nudtportal.framework.constant.ProjectConst;
import alp.starcode.nudtportal.framework.login.user.User;
import alp.starcode.nudtportal.framework.model.dto.RequestExceptionDTO;
import alp.starcode.nudtportal.framework.model.dto.UserBehaviorDTO;
import alp.starcode.nudtportal.framework.model.dto.UserLoginLogIDTO;
import alp.starcode.nudtportal.framework.mybatis.dao.impl.*;
import alp.starcode.nudtportal.framework.mybatis.entity.*;
import alp.starcode.nudtportal.framework.mybatis.vo.LogModuleAccessVO;
import alp.starcode.nudtportal.framework.util.IPUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.ElasticsearchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.UUID;


/**
 * 用户相关接口逻辑实现层
 *
 * @author hyc
 * @date 2022-05-26
 */
@Slf4j
@Service
public class UserAccountService extends BaseService {


    @Autowired
    ModuleDisplayClassifyDaoImpl displayClassifyDao;

    @Autowired
    LogModuleAccessDaoImpl logModuleAccessDao;

    @Autowired
    ModuleVisitDaoImpl moduleVisitDao;

    @Autowired
    ElasticsearchRestTemplate restTemplate;

    @Resource
    UserAccountDaoImpl userAccountDao;

    @Resource
    LogUserLoginDaoImpl logUserLoginDao;


    /**
     * 记录用户行为日志
     *
     * @param userBehaviorDTO
     */
    @Transactional(rollbackFor = Exception.class)
    @Async
    public Boolean recordUserBehavior(UserBehaviorDTO userBehaviorDTO) {
        LogModuleAccess logModuleAccess = BeanUtils.copyProperties(userBehaviorDTO, LogModuleAccess.class);
        //访问流水id
        logModuleAccess.setModuleAccessId(UUID.randomUUID().toString());
        //查询模块分类信息
        String moduleClass = userBehaviorDTO.getModuleClass();
        //搜索信息
        String searchStr = userBehaviorDTO.getSearchStr();
        //说明是搜索进来的
        if (StringUtils.isEmpty(moduleClass) && !StringUtils.isEmpty(searchStr)) {
            logModuleAccess.setSearchStr(searchStr);
        } else {
            ModuleDisplayClassify displayClassify = displayClassifyDao
                    .getOne(new LambdaQueryWrapper<ModuleDisplayClassify>()
                            .eq(ModuleDisplayClassify::getClassifyName, moduleClass));
            if (displayClassify != null) {
                //模块分类id
                logModuleAccess.setModuleClassUuid(displayClassify.getClassifyId());
                //模块分类名称
                logModuleAccess.setModuleClass(displayClassify.getClassifyName());
            }
        }
        //模块id
        String moduleId = userBehaviorDTO.getModuleId();
        long timeMillis = System.currentTimeMillis();
        logModuleAccess.setCreateTime(timeMillis);
        logModuleAccess.setUpdateTime(timeMillis);
        boolean flag = false;
        //新增日志
        flag = logModuleAccessDao.save(logModuleAccess);
        if (!StringUtils.isEmpty(moduleId)) {
            //新增模块访问量
            flag = addModuleVisit(moduleId);
        }
        //新增到es
        addESLogModuleAccess(logModuleAccess);
        return flag;
    }

    /**
     * 用户行为新增到es
     *
     * @param logModuleAccess 用户行为
     */
    private void addESLogModuleAccess(LogModuleAccess logModuleAccess) {
        try {
            LogModuleAccessVO logDataExchangeVO = BeanUtils.copyProperties(logModuleAccess, LogModuleAccessVO.class);
            restTemplate.save(logDataExchangeVO);
        } catch (Exception e) {
            log.error("用户行为日志入ES失败，异常原因：{}", e.getMessage());
        }
    }

    /**
     * 新增模块访问量
     *
     * @param moduleId 模块id
     */
    private boolean addModuleVisit(String moduleId) {
        //获取当天凌晨毫秒值
        long recordTime = LocalDate.now()
                .atStartOfDay(ZoneOffset.ofHours(8))
                .toInstant()
                .toEpochMilli();
        //根据凌晨时间和模块id查询
        LambdaQueryWrapper<ModuleVisit> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ModuleVisit::getModuleId, moduleId).eq(ModuleVisit::getRecordTime, recordTime);
        ModuleVisit moduleVisit = moduleVisitDao.getOne(lambdaQueryWrapper);
        if (moduleVisit == null) {
            moduleVisit = new ModuleVisit();
            moduleVisit.setModuleId(moduleId);
            moduleVisit.setRecordTime(recordTime);
            moduleVisit.setVisitSize(1L);
        } else {
            moduleVisit.setVisitSize(moduleVisit.getVisitSize() + 1);
        }
        moduleVisit.setUpdateTime(System.currentTimeMillis());
        return moduleVisitDao.saveOrUpdate(moduleVisit);
    }

    /**
     * 保存用户登录失败日志信息
     *
     * @param userLoginLogIDTO 用户信息
     * @return true/false
     *
     */
    public Boolean recordUserLoginInfo(UserLoginLogIDTO userLoginLogIDTO) {
        // 从请求头中获取用户IP
        ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String loginName = userLoginLogIDTO.getLoginName();
        UserAccount account = userAccountDao.getOne(new LambdaQueryWrapper<UserAccount>().select(UserAccount::getUserId)
                .eq(UserAccount::getCampusNumber, userLoginLogIDTO.getLoginName()));
        if (account != null) {
            HttpServletRequest request = sra.getRequest();
            LogUserLogin login = new LogUserLogin();
            login.setLoginId(UUID.randomUUID().toString());
            login.setUserId(account.getUserId());
            login.setLoginAddress(IPUtil.getIp(request));
            login.setLoginName(userLoginLogIDTO.getLoginName());
            login.setLoginTime(System.currentTimeMillis());
            login.setBrowserType(userLoginLogIDTO.getBrowserType());
            login.setCampusNumber(userLoginLogIDTO.getLoginName());
            login.setCreateTime(System.currentTimeMillis());
            login.setUpdateTime(System.currentTimeMillis());
            login.setLoginSuccess(ProjectConst.IS_DELETED_Y);
            logUserLoginDao.save(login);
            try {
                restTemplate.save(login);
            } catch (ElasticsearchException e) {
                log.error("es出现错误，原因是：{}", e.getMessage());
            } catch (Exception e) {
                log.error("es添加用户登录日志失败,原因是:{}", e.getMessage());
            }
        }
        return true;
    }


    /**
     * 记录用户请求异常信息
     *
     * @param requestExceptionDTO 异常信息参数列表
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Boolean recordUserRequestException(RequestExceptionDTO requestExceptionDTO) {
        try {
            User user = getCurrentUser();
            LogRequestException requestException = BeanUtils.copyProperties(requestExceptionDTO, LogRequestException.class);
            //填入必填项并插入数据
            requestException.setExceptionId(UUID.randomUUID().toString()).setUserId(user.getUserId())
                    .setCampusNumber(user.getCampusNumber()).setLoginName(user.getLoginAccount())
                    .setCreateTime(System.currentTimeMillis()).setUpdateTime(System.currentTimeMillis());
            //添加es数据
            restTemplate.save(requestException);
            return requestException.insert();
        } catch (ElasticsearchException e) {
            log.error("es出现错误，原因是：{}", e.getMessage());
        } catch (Exception e) {
            log.error("添加请求异常日志失败,原因是:{}", e.getMessage());
        }
        return true;
    }
}
